﻿// 
//   SubSonic - http://subsonicproject.com
// 
//   The contents of this file are subject to the New BSD
//   License (the "License"); you may not use this file
//   except in compliance with the License. You may obtain a copy of
//   the License at http://www.opensource.org/licenses/bsd-license.php
//  
//   Software distributed under the License is distributed on an 
//   "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
//   implied. See the License for the specific language governing
//   rights and limitations under the License.
// 
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace PleaseORM.Extensions
{
	public static class Strings
	{
		private static readonly Dictionary<int, string> _entityTable = new Dictionary<int, string>();
		private static readonly Dictionary<string, string> _USStateTable = new Dictionary<string, string>();

		/// <summary>
		/// Initializes the <see cref="Strings"/> class.
		/// </summary>
		static Strings()
		{
		}

		public static bool Matches(this string source, string compare)
		{
			return String.Equals(source, compare, StringComparison.InvariantCultureIgnoreCase);
		}

		public static bool MatchesTrimmed(this string source, string compare)
		{
			return String.Equals(source.Trim(), compare.Trim(), StringComparison.InvariantCultureIgnoreCase);
		}

		public static bool MatchesRegex(this string inputString, string matchPattern)
		{
			return Regex.IsMatch(inputString, matchPattern,
					RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
		}

		/// <summary>
		/// Strips the last specified chars from a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="removeFromEnd">The remove from end.</param>
		/// <returns></returns>
		public static string Chop(this string sourceString, int removeFromEnd)
		{
			string result = sourceString;
			if ((removeFromEnd > 0) && (sourceString.Length > removeFromEnd - 1))
				result = result.Remove(sourceString.Length - removeFromEnd, removeFromEnd);
			return result;
		}

		/// <summary>
		/// Strips the last specified chars from a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="backDownTo">The back down to.</param>
		/// <returns></returns>
		public static string Chop(this string sourceString, string backDownTo)
		{
			int removeDownTo = sourceString.LastIndexOf(backDownTo);
			int removeFromEnd = 0;
			if (removeDownTo > 0)
				removeFromEnd = sourceString.Length - removeDownTo;

			string result = sourceString;

			if (sourceString.Length > removeFromEnd - 1)
				result = result.Remove(removeDownTo, removeFromEnd);

			return result;
		}

		/// <summary>
		/// Plurals to singular.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string PluralToSingular(this string sourceString)
		{
			return sourceString.MakeSingular();
		}

		/// <summary>
		/// Singulars to plural.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string SingularToPlural(this string sourceString)
		{
			return sourceString.MakePlural();
		}

		/// <summary>
		/// Make plural when count is not one
		/// </summary>
		/// <param name="number">The number of things</param>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string Pluralize(this int number, string sourceString)
		{
			if (number == 1)
				return String.Concat(number, " ", sourceString.MakeSingular());
			return String.Concat(number, " ", sourceString.MakePlural());
		}

		/// <summary>
		/// Removes the specified chars from the beginning of a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="removeFromBeginning">The remove from beginning.</param>
		/// <returns></returns>
		public static string Clip(this string sourceString, int removeFromBeginning)
		{
			string result = sourceString;
			if (sourceString.Length > removeFromBeginning)
				result = result.Remove(0, removeFromBeginning);
			return result;
		}

		/// <summary>
		/// Removes chars from the beginning of a string, up to the specified string
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="removeUpTo">The remove up to.</param>
		/// <returns></returns>
		public static string Clip(this string sourceString, string removeUpTo)
		{
			int removeFromBeginning = sourceString.IndexOf(removeUpTo);
			string result = sourceString;

			if (sourceString.Length > removeFromBeginning && removeFromBeginning > 0)
				result = result.Remove(0, removeFromBeginning);

			return result;
		}

		/// <summary>
		/// Strips the last char from a a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string Chop(this string sourceString)
		{
			return Chop(sourceString, 1);
		}

		/// <summary>
		/// Strips the last char from a a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string Clip(this string sourceString)
		{
			return Clip(sourceString, 1);
		}

		/// <summary>
		/// Fasts the replace.
		/// </summary>
		/// <param name="original">The original.</param>
		/// <param name="pattern">The pattern.</param>
		/// <param name="replacement">The replacement.</param>
		/// <returns></returns>
		public static string FastReplace(this string original, string pattern, string replacement)
		{
			return FastReplace(original, pattern, replacement, StringComparison.InvariantCultureIgnoreCase);
		}

		/// <summary>
		/// Fasts the replace.
		/// </summary>
		/// <param name="original">The original.</param>
		/// <param name="pattern">The pattern.</param>
		/// <param name="replacement">The replacement.</param>
		/// <param name="comparisonType">Type of the comparison.</param>
		/// <returns></returns>
		public static string FastReplace(this string original, string pattern, string replacement,
																		 StringComparison comparisonType)
		{
			if (original == null)
				return null;

			if (String.IsNullOrEmpty(pattern))
				return original;

			int lenPattern = pattern.Length;
			int idxPattern = -1;
			int idxLast = 0;

			StringBuilder result = new StringBuilder();

			while (true)
			{
				idxPattern = original.IndexOf(pattern, idxPattern + 1, comparisonType);

				if (idxPattern < 0)
				{
					result.Append(original, idxLast, original.Length - idxLast);
					break;
				}

				result.Append(original, idxLast, idxPattern - idxLast);
				result.Append(replacement);

				idxLast = idxPattern + lenPattern;
			}

			return result.ToString();
		}

		/// <summary>
		/// Returns text that is located between the startText and endText tags.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="startText">The text from which to start the crop</param>
		/// <param name="endText">The endpoint of the crop</param>
		/// <returns></returns>
		public static string Crop(this string sourceString, string startText, string endText)
		{
			int startIndex = sourceString.IndexOf(startText, StringComparison.CurrentCultureIgnoreCase);
			if (startIndex == -1)
				return String.Empty;

			startIndex += startText.Length;
			int endIndex = sourceString.IndexOf(endText, startIndex, StringComparison.CurrentCultureIgnoreCase);
			if (endIndex == -1)
				return String.Empty;

			return sourceString.Substring(startIndex, endIndex - startIndex);
		}

		/// <summary>
		/// Removes excess white space in a string.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string Squeeze(this string sourceString)
		{
			char[] delim = { ' ' };
			string[] lines = sourceString.Split(delim, StringSplitOptions.RemoveEmptyEntries);
			StringBuilder sb = new StringBuilder();
			foreach (string s in lines)
			{
				if (!String.IsNullOrEmpty(s.Trim()))
					sb.Append(s + " ");
			}
			//remove the last pipe
			string result = Chop(sb.ToString());
			return result.Trim();
		}

		/// <summary>
		/// Removes all non-alpha numeric characters in a string
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string ToAlphaNumericOnly(this string sourceString)
		{
			return Regex.Replace(sourceString, @"\W*", "");
		}

		/// <summary>
		/// Creates a string array based on the words in a sentence
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <returns></returns>
		public static string[] ToWords(this string sourceString)
		{
			string result = sourceString.Trim();
			return result.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
		}

		/// <summary>
		/// Strips all HTML tags from a string
		/// </summary>
		/// <param name="htmlString">The HTML string.</param>
		/// <returns></returns>
		public static string StripHTML(this string htmlString)
		{
			return StripHTML(htmlString, String.Empty);
		}

		/// <summary>
		/// Strips all HTML tags from a string and replaces the tags with the specified replacement
		/// </summary>
		/// <param name="htmlString">The HTML string.</param>
		/// <param name="htmlPlaceHolder">The HTML place holder.</param>
		/// <returns></returns>
		public static string StripHTML(this string htmlString, string htmlPlaceHolder)
		{
			const string pattern = @"<(.|\n)*?>";
			string sOut = Regex.Replace(htmlString, pattern, htmlPlaceHolder);
			sOut = sOut.Replace("&nbsp;", String.Empty);
			sOut = sOut.Replace("&amp;", "&");
			sOut = sOut.Replace("&gt;", ">");
			sOut = sOut.Replace("&lt;", "<");
			return sOut;
		}

		public static List<string> FindMatches(this string source, string find)
		{
			Regex reg = new Regex(find, RegexOptions.IgnoreCase);

			List<string> result = new List<string>();
			foreach (Match m in reg.Matches(source))
				result.Add(m.Value);
			return result;
		}

		/// <summary>
		/// Converts a generic List collection to a single comma-delimitted string.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <returns></returns>
		public static string ToDelimitedList(this IEnumerable<string> list)
		{
			return ToDelimitedList(list, ",");
		}

		/// <summary>
		/// Converts a generic List collection to a single string using the specified delimitter.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="delimiter">The delimiter.</param>
		/// <returns></returns>
		public static string ToDelimitedList(this IEnumerable<string> list, string delimiter)
		{
			StringBuilder sb = new StringBuilder();
			foreach (string s in list)
				sb.Append(String.Concat(s, delimiter));
			string result = sb.ToString();
			result = Chop(result);
			return result;
		}

		/// <summary>
		/// Strips the specified input.
		/// </summary>
		/// <param name="sourceString">The source string.</param>
		/// <param name="stripValue">The strip value.</param>
		/// <returns></returns>
		public static string Strip(this string sourceString, string stripValue)
		{
			if (!String.IsNullOrEmpty(stripValue))
			{
				string[] replace = stripValue.Split(new[] { ',' });
				for (int i = 0; i < replace.Length; i++)
				{
					if (!String.IsNullOrEmpty(sourceString))
						sourceString = Regex.Replace(sourceString, replace[i], String.Empty);
				}
			}
			return sourceString;
		}

		/// <summary>
		/// Converts ASCII encoding to Unicode
		/// </summary>
		/// <param name="asciiCode">The ASCII code.</param>
		/// <returns></returns>
		public static string AsciiToUnicode(this int asciiCode)
		{
			Encoding ascii = Encoding.UTF32;
			char c = (char)asciiCode;
			Byte[] b = ascii.GetBytes(c.ToString());
			return ascii.GetString((b));
		}

		/// <summary>
		/// Converts Text to HTML-encoded string
		/// </summary>
		/// <param name="textString">The text string.</param>
		/// <returns></returns>
		public static string TextToEntity(this string textString)
		{
			foreach (KeyValuePair<int, string> key in _entityTable)
				textString = textString.Replace(AsciiToUnicode(key.Key), key.Value);
			return textString.Replace(AsciiToUnicode(38), "&amp;");
		}

		/// <summary>
		/// Converts HTML-encoded bits to Text
		/// </summary>
		/// <param name="entityText">The entity text.</param>
		/// <returns></returns>
		public static string EntityToText(this string entityText)
		{
			entityText = entityText.Replace("&amp;", "&");
			foreach (KeyValuePair<int, string> key in _entityTable)
				entityText = entityText.Replace(key.Value, AsciiToUnicode(key.Key));
			return entityText;
		}

		/// <summary>
		/// Formats the args using String.Format with the target string as a format string.
		/// </summary>
		/// <param name="fmt">The format string passed to String.Format</param>
		/// <param name="args">The args passed to String.Format</param>
		/// <returns></returns>
		public static string ToFormattedString(this string fmt, params object[] args)
		{
			return String.Format(fmt, args);
		}

		/// <summary>
		/// Strings to enum.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="Value">The value.</param>
		/// <returns></returns>
		public static T ToEnum<T>(this string Value)
		{
			T oOut = default(T);
			Type t = typeof(T);
			foreach (FieldInfo fi in t.GetFields())
			{
				if (fi.Name.Matches(Value))
					oOut = (T)fi.GetValue(null);
			}

			return oOut;
		}

		public static bool IsUpperCase(this string input)
		{
			for (int i = 0; i < input.Length; i++)
			{
				if (string.Compare(input.Substring(i, 1), input.Substring(i, 1).ToUpper(), false) != 0)
					return false;
			}
			return true;
		}

	}
}